package com.quarkdown.core.ast.attributes

import com.quarkdown.core.ast.NestableNode
import com.quarkdown.core.ast.Node
import com.quarkdown.core.ast.attributes.location.LocationTrackableNode
import com.quarkdown.core.ast.base.block.LinkDefinition
import com.quarkdown.core.ast.quarkdown.FunctionCallNode
import com.quarkdown.core.context.toc.TableOfContents
import com.quarkdown.core.property.AssociatedProperties
import com.quarkdown.core.property.MutableAssociatedProperties
import com.quarkdown.core.property.MutablePropertyContainer
import com.quarkdown.core.property.PropertyContainer

/**
 * Additional information about the node tree, produced by the parsing stage and stored in a [com.quarkdown.core.context.Context].
 * @see com.quarkdown.core.context.Context
 */
interface AstAttributes {
    /**
     * The root node of the tree.
     */
    val root: NestableNode?

    /**
     * Properties associated with nodes in the AST.
     * These properties enrich the AST by storing additional information about the nodes, such as:
     * - [com.quarkdown.core.ast.attributes.location.SectionLocationProperty] for tracking the location of [LocationTrackableNode]s;
     * - [com.quarkdown.core.ast.attributes.location.LocationLabelProperty] for storing formatted labels of [LocationTrackableNode]s;
     * - [com.quarkdown.core.ast.media.StoredMediaProperty] for attaching [com.quarkdown.core.media.Media] references.
     */
    val properties: AssociatedProperties<Node, Any>

    /**
     * @see AssociatedProperties.of on [properties]
     */
    fun of(node: Node): PropertyContainer<Any> = properties.of(node)

    /**
     * Properties associated with third-party elements in the AST.
     * These properties are used to track the presence of third-party elements in the AST,
     * in order to conditionally load third-party libraries in the final artifact
     * to avoid unnecessary bloat and improve performance.
     *
     * These properties are updated during the tree traversal stage of the pipeline.
     * @see com.quarkdown.core.ast.attributes.presence for properties
     * @see com.quarkdown.core.context.hooks.presence for hooks that scan the AST and set these properties
     */
    val thirdPartyPresenceProperties: PropertyContainer<Boolean>

    /**
     * The defined links, which can be referenced by other nodes.
     */
    val linkDefinitions: List<LinkDefinition>

    /**
     * The function calls to be later executed.
     */
    val functionCalls: List<FunctionCallNode>

    /**
     * The table of contents of all the headings in the document.
     * This is generated by the tree traversal stage of the pipeline.
     */
    val tableOfContents: TableOfContents?

    /**
     * @return a new copied mutable instance of these attributes
     */
    fun toMutable(): MutableAstAttributes
}

/**
 * Writeable attributes that are modified during the parsing process,
 * and carry useful information for the next stages of the pipeline.
 * Storing these attributes while parsing prevents a further visit of the final tree.
 * @param root the root node of the tree. According to the architecture, this is set right after the parsing stage
 * @param linkDefinitions the defined links, which can be referenced by other nodes
 * @param functionCalls the function calls to be later executed
 * @param hasCode whether there is at least one code block.
 * @param hasMath whether there is at least one math block or inline.
 * @see com.quarkdown.core.context.MutableContext
 */
data class MutableAstAttributes(
    override var root: NestableNode? = null,
    override val properties: MutableAssociatedProperties<Node, Any> = MutableAssociatedProperties(),
    override val thirdPartyPresenceProperties: MutablePropertyContainer<Boolean> = MutablePropertyContainer(),
    override val linkDefinitions: MutableList<LinkDefinition> = mutableListOf(),
    override val functionCalls: MutableList<FunctionCallNode> = mutableListOf(),
    override var tableOfContents: TableOfContents? = null,
) : AstAttributes {
    override fun of(node: Node): MutablePropertyContainer<Any> = properties.of(node)

    override fun toMutable(): MutableAstAttributes = this.copy()
}
